home *** CD-ROM | disk | FTP | other *** search
- {**********************************************************************
-
- MDI Program Support Objects $Version$
- $Author$ $Date$
-
- Copyright (c) 1991 Anthony M. Vitabile.
- All rights reserved.
-
- Permission is hereby granted to all who desire to use this
- source code in their programs provided the above copyright
- is included in the program.
-
- Unit Description
-
- This unit implements three basic object types for use in MDI
- programs under Windows. These are:
-
- TMDIChild: A normal MDI child window which supports the use of
- a special menu whenever the window is active;
-
- TMDIDialog: An MDI window which contains a modeless dialog box
- as its only child. The window adjusts itself so that it looks
- like the non-client portion of a dialog box.
-
- TMDIFrame: A frame window type descended from TMDIWindow that
- supports locating and closing a particular child window when
- that child window's handle is provided to it. This can serve
- as the basis for a method that closes the currently active
- MDI child window.
-
- **********************************************************************}
-
- Unit MDITypes;
- Interface
- Uses WinTypes, WObjects;
-
- type
- PMDIChild = ^TMDIChild;
- PMDIDialog = ^TMDIDialog;
- PMDIFrame = ^TMDIFrame;
-
- TMDIChild = object (TWindow)
- MenuPos: integer;
- Menu ,
- WndMenu: HWnd;
-
- constructor Init(AParent : PWindowsObject;
- ATitle : PChar;
- AMenu : HMenu;
- AMenuPos: integer);
- constructor Load(var S: TStream);
- procedure Store(var S: TStream);
- procedure wmMDIActivate(var Msg: TMessage);
- virtual wm_First + wm_MDIActivate;
- end;
-
- TMDIDialog = object (TMDIChild)
- SysMenu : HMenu;
- NCHeight : integer;
- TheDialog: PDlgWindow;
-
- procedure ActivateDlg(var Msg: TMessage);
- destructor Done;
- virtual;
- constructor Init(AParent : PWindowsObject;
- ATitle : PChar;
- AMenu : HMenu;
- AMenuPos: integer);
- function InitDialog: PDlgWindow;
- virtual;
- constructor Load(var S: TStream);
- procedure Store(var S: TStream);
- procedure SetChildFocus(var Msg: TMessage);
- procedure SetupWindow;
- virtual;
- procedure wmActivate(var Msg: TMessage);
- virtual wm_First + wm_Activate;
- procedure wmChildActivate(var Msg: TMessage);
- virtual wm_First + wm_ChildActivate;
- procedure wmInitMenu(var Msg: TMessage);
- virtual wm_First + wm_InitMenu;
- procedure wmGetText(var Msg: TMessage);
- virtual wm_First + wm_GetText;
- procedure wmMDIActivate(var Msg: TMessage);
- virtual wm_First + wm_MDIActivate;
- procedure wmSetFocus(var Msg: TMessage);
- virtual wm_First + wm_SetFocus;
- procedure wmSize(var Msg: TMessage);
- virtual wm_First + wm_Size;
- procedure wmSysCommand(var Msg: TMessage);
- virtual wm_First + wm_SysCommand;
- end;
-
- TMDIFrame = object (TMDIWindow)
- procedure CloseChild(AChild: PWindowsObject);
- virtual;
- function GetChild(TheWnd: HWnd): PWindowsObject;
- virtual;
- procedure wmInitMenu(var Msg: TMessage);
- virtual wm_First + wm_InitMenu;
- procedure wmInitMenuPopup(var Msg: TMessage);
- virtual wm_First + wm_InitMenuPopup;
- end;
-
- Implementation
- Uses Strings, WinProcs;
-
- const
- RMDIChild : TStreamRec = (ObjType: 10000;
- VmtLink: Ofs(TypeOf(TMDIChild)^);
- Load: @TMDIChild.Load;
- Store: @TMDIChild.Store);
-
- RMDIDialog: TStreamRec = (ObjType: 10001;
- VmtLink: Ofs(TypeOf(TMDIDialog)^);
- Load: @TMDIDialog.Load;
- Store: @TMDIDialog.Store);
-
- RMDIFrame : TStreamRec = (ObjType: 10002;
- VmtLink: Ofs(TypeOf(TMDIFrame)^);
- Load: @TMDIFrame.Load;
- Store: @TMDIFrame.Store);
-
- {**********************************************************************
-
- TMDIChild Methods
-
- Init Constructor. Creates & initlializes an instance of
- TMDIChild. Most of the window's initialization is passed onto
- TWindow, the ancestor object type of this type. We save the
- child's frame menu handle in the inherited Attr.Menu field, then
- save the handle of the Window popup menu, whose position in the
- menu is given by AMenuPos.
-
- **********************************************************************}
-
- constructor TMDIChild.Init(AParent : PWindowsObject;
- ATitle : PChar;
- AMenu : HMenu;
- AMenuPos: integer);
- begin { TMDIChild.Init }
- TWindow.Init(AParent, ATitle);
- Menu := AMenu;
- MenuPos := AMenuPos;
- if Menu = 0
- then WndMenu := 0
- else WndMenu := GetSubMenu(Menu, MenuPos)
- end { TMDIChild.Init };
-
- {**********************************************************************
-
- Load Constructor. Creates & initlializes an instance of
- TMDIChild that has been read from a stream.
-
- **********************************************************************}
-
- constructor TMDIChild.Load(var S: TStream);
- begin { TMDIChild.Load }
- TWindow.Load(S);
- S.Read(Menu , sizeof(Menu));
- S.Read(MenuPos, sizeof(MenuPos));
- WndMenu := 0
- end { TMDIChild.Load };
-
- {**********************************************************************
-
- Store method. Writes an instance of a TMDIChild onto the stream.
-
- **********************************************************************}
-
- procedure TMDIChild.Store(var S: TStream);
- begin { TMDIChild.Store }
- TWindow.Store(S);
- S.Write(Menu , sizeof(Menu));
- S.Write(MenuPos, sizeof(MenuPos))
- end { TMDIChild.Store };
-
- {**********************************************************************
-
- wmMDIActivate message handler. This method is executed whenever
- the MDI child window receives a wm_MDIActivate message. The
- action taken is to reset the menu currently being used by the
- current frame window to either the menu specified by the
- Attr.Menu field of this TMDIChild instance if the window is
- being activated (wParam <> 0), or to reset it to the default
- menu for the frame as specified by the frame's Attr.Menu field.
-
- Note that in either case, if the handle of the new menu is 0,
- then the current menu is not changed!
-
- **********************************************************************}
-
- procedure TMDIChild.wmMDIActivate(var Msg: TMessage);
- var
- NewMenu ,
- NewPos : HMenu;
- MDIClient : PMDIClient;
- MDIClientWnd: HWnd;
-
- begin { TMDIChild.wmMDIActivate }
- MDIClient := PMDIWindow(Parent)^.GetClient;
- MDIClientWnd := MDIClient^.HWindow;
- if Msg.WParam = 0
- then
- begin { Get frame window menu data }
- NewMenu := PMDIWindow(Parent)^.Attr.Menu;
- NewPos := MDIClient^.ClientAttr.hWindowMenu
- end
- else
- begin { Get MDI child window menu data }
- NewMenu := Menu;
- NewPos := WndMenu
- end; { Now Set the menu up }
- if NewMenu <> 0
- then
- begin
- SendMessage(MDIClientWnd, wm_MDISetMenu, 0, MakeLong(NewMenu, NewPos));
- DrawMenuBar(Parent^.HWindow) { Redraw the menu bar }
- end;
- Msg.Result := 0 { Return 0 }
- end { TMDIChild.wmMDIActivate };
-
- {**********************************************************************
-
- TMDIDialog methods
-
- Activate the child dialog box method. This method is called
- whenever the MDI dialog is activated or receives a message which
- affects the keyboard handling. If the window is not an icon,
- then the current keyboard handler is set to the window's child
- dialog box. Otherwise, the keyboard handler is set to nil.
-
- **********************************************************************}
-
- procedure TMDIDialog.ActivateDlg(var Msg: TMessage);
- begin { TMDIDialog.ActivateDlg }
- if IsIconic(HWindow) { If the window isn't an icon right now }
- then Application^.SetKBHandler(nil) { Otherwise turn off the keyboard handler }
- else Application^.SetKBHandler(TheDialog); { Turn on the dialog's keyboard handler }
- Msg.Result := 0
- end { TMDIDialog.ActivateDlg };
-
- {**********************************************************************
-
- Destroys a TMDIDialog. The routine first disposes of the
- child dialog box, then destroys itself.
-
- **********************************************************************}
-
- destructor TMDIDialog.Done;
- begin { TMDIDialog.Done }
- dispose(TheDialog, Done);
- Menu := 0;
- WndMenu := 0;
- Sysmenu := 0;
- TMDIChild.Done
- end { TMDIDialog.Done };
-
- {**********************************************************************
-
- Init contructor. This method creates a new TMDIDialog. It
- allows TMDIChild to do most of the initialization, and then it
- initializes the handle of the window's system menu to 0. It
- then calls the virtual method InitDialog to actually create
- an OWL TDlgWindow object.
-
- **********************************************************************}
-
- constructor TMDIDialog.Init(AParent : PWindowsObject;
- ATitle : PChar;
- AMenu : HMenu;
- AMenuPos: integer);
- begin { TMDIDialog.Init }
- TMDIChild.Init(AParent, ATitle, AMenu, AMenuPos);
- SysMenu := 0;
- TheDialog := InitDialog
- end { TMDIDialog.Init };
-
- {**********************************************************************
-
- InitDialog method. This method creates a TDlgWindow, which
- is to be the child of the TMDIDialog window. This is an
- abstract method which must be overridden by a descendant type.
-
- **********************************************************************}
-
- function TMDIDialog.InitDialog: PDlgWindow;
- begin { TMDIDialog.InitDialog }
- abstract
- end { TMDIDialog.InitDialog };
-
- {**********************************************************************
-
- Load Constructor. Creates & initlializes an instance of
- TMDIDialog that has been read from a stream.
-
- **********************************************************************}
-
- constructor TMDIDialog.Load(var S: TStream);
- begin { TMDIDialog.Load }
- TMDIChild.Load(S);
- GetChildPtr(S, TheDialog)
- end { TMDIChild.Load };
-
- {**********************************************************************
-
- Store method. Writes an instance of a TMDIDialog onto the stream.
-
- **********************************************************************}
-
- procedure TMDIDialog.Store(var S: TStream);
- begin { TMDIDialog.Store }
- TMDIChild.Store(S);
- PutChildPtr(S, TheDialog)
- end { TMDIDialog.Store };
-
- {**********************************************************************
-
- SetChildFocus method. This method is responsible for saving
- and properly restoring the focus to the TDlgWindow's child
- controls once the TMDIDialog gains or loses activation or
- focus. The method uses the TMDIDialog's FocusChildHandle
- field, inherited from TWindow, to track the handle of the
- dialog's child that has the focus at the time the window is
- deactivated.
-
- **********************************************************************}
-
- procedure TMDIDialog.SetChildFocus(var Msg: TMessage);
- var
- CurrentFocus,
- Dlg : HWnd;
-
- begin { TMDIDialog.SetChildFocus }
- Dlg := TheDialog^.HWindow; { Make life easier for us }
- if not IsIconic(HWindow) { If the window isn't an icon right now }
- then
- if Msg.wParam <> 0 { If the window is being activated }
- then { Set the focus to the correct child handle }
- begin
- if FocusChildHandle = 0 { If we haven't got any focus yet }
- then SetFocus(TheDialog^.HWindow) { Then set the focus to the dialog }
- else { Set the focus to the correct window }
- if IsWindow(FocusChildHandle) { Make sure we have a valid handle here! }
- then SetFocus(FocusChildHandle)
- end
- else { Save the current focus }
- begin
- CurrentFocus := GetFocus; { Get the window that current has focus }
- if (CurrentFocus <> 0) and { If there is one that has the focus }
- IsChild(Dlg, CurrentFocus) { And it's a child of the dialog }
- then FocusChildHandle := CurrentFocus { Then save the child's handle }
- end;
- Msg.Result := 0
- end { TMDIDialog.SetChildFocus };
-
- {**********************************************************************
-
- SetupWindow. Creates all window elements and then sets the
- focus to the child TDlgWindow dialog box. It then initializes
- FocusChildHandle to the handle of the window that currently has
- the focus.
-
- **********************************************************************}
-
- procedure TMDIDialog.SetupWindow;
- var
- h ,
- w : integer;
- Rect: TRect;
-
- begin { TMDIDialog.SetupWindow }
- TMDIChild.SetupWindow; { Do ancestor setup }
- SetFocus(TheDialog^.HWindow); { Set the focus to the dialog }
- GetWindowRect(HWindow, Rect); { Get the window's rectangle }
- h := Rect.bottom - Rect.top; { Compute the rectangle's height }
- GetClientRect(HWindow, Rect); { Now get the window's client rectangle }
- NCHeight := h - Rect.bottom - Rect.top; { The correct Non-Client height is the height difference }
- GetClientRect(TheDialog^.HWindow, Rect); { Finally, get the dialog box's client rectangle }
- Attr.H := Rect.bottom - Rect.top + NCHeight;
- Attr.W := Rect.right - Rect.left
- end { TMDIDialog.SetupWindow };
-
-
- {**********************************************************************
-
- Track the window's activation state. When this message is
- received, the window must activate the child dialog and then
- set the child focus. This message is received when the parent
- application is restored from an icon. The top most MDI child
- gets the message immediately following the restoration of
- the Frame window.
-
- **********************************************************************}
-
- procedure TMDIDialog.wmActivate(var Msg: TMessage);
- begin { TMDIDialog.wmActivate }
- ActivateDlg (Msg);
- SetChildFocus(Msg);
- DefWndProc (Msg)
- end { TMDIDialog.wmActivate };
-
- {**********************************************************************
-
- We get this message because Windows is playing with the dialog
- window. This usually happens during processing of wm_Activate
- and wm_MDIActivate. We activate the child TDlgWindow dialog
- box's keyboard handler after first passing it onto the default
- message procedure for the window.
-
- NOTE THAT WE LEAVE THE FOCUS ALONE!
-
- **********************************************************************}
-
- procedure TMDIDialog.wmChildActivate(var Msg: TMessage);
- begin { TMDIDialog.wmChildActivate }
- ActivateDlg(Msg);
- DefWndProc (Msg)
- end { TMDIDialog.wmChildActivate };
-
- {**********************************************************************
-
- This method adds code to the wm_GetText handling that stores
- current dialog box child window control handle. This is done
- here to handle the case where the current dialog box has the
- focus & control is switched away from the application without
- minimizing it.
-
- It is *important* that this method call DefWndProc!!!!!!!!
-
- **********************************************************************}
-
- procedure TMDIDialog.wmGetText(var Msg: TMessage);
- var
- CurFocus: HWnd;
-
- begin { TMDIDialog.wmGetText }
- CurFocus := GetFocus; { Get the window that current has focus }
- if (CurFocus <> 0) and { If there is one that has the focus }
- IsChild(TheDialog^.HWindow, CurFocus) { And it's a child of the dialog }
- then FocusChildHandle := CurFocus; { Then save the child's handle }
- DefWndProc(Msg)
- end { TMDIDialog.wmGetText };
-
- {**********************************************************************
-
- This method handles saving the focus for the current dialog
- child window when a menu item is selected. This helps to ensure
- that the focus goes to the right control at all times.
-
- The method also disables the Maximize and Size options on the
- child window's system menu. This allows the code to prevent
- the window's size from being changed.
-
- **********************************************************************}
-
- procedure TMDIDialog.wmInitMenu(var Msg: TMessage);
- var
- CurFocus: HWnd;
-
- begin { TMDIDialog.wmInitMenu }
- CurFocus := GetFocus; { Get the window that current has focus }
- if (CurFocus <> 0) and { If there is one that has the focus }
- IsChild(TheDialog^.HWindow, CurFocus) { And it's a child of the dialog }
- then FocusChildHandle := CurFocus; { Then save the child's handle }
- if SysMenu = 0
- then SysMenu := GetSystemMenu(HWindow, FALSE); { Get the window's system menu }
- EnableMenuItem(SysMenu, sc_Maximize, { Disable the maximize menu item }
- mf_Grayed or mf_ByCommand);
- EnableMenuItem(SysMenu, sc_Size, { Disable the sizing menu item }
- mf_Grayed or mf_ByCommand);
- DefWndProc(Msg)
- end { TMDIDialog.wmInitMenu };
-
- {**********************************************************************
-
- This method handles the case of the TMDIDialog window being
- activated or deactivated. The message is handled by first
- calling the TMDIChild method of the same name, so that the
- menu can be reset. We then activate the child dialog box's
- keyboard handler and then set the focus to the correct child
- control.
-
- **********************************************************************}
-
- procedure TMDIDialog.wmMDIActivate(var Msg: TMessage);
- begin { TMDIDialog.wm_MDIActivate }
- TMDIChild.wmMDIActivate(Msg); { Change the frame window's menu }
- ActivateDlg (Msg); { Activate the dialog's keyboard handler }
- SetChildFocus(Msg) { Reset the focus to the correct control }
- end { TMDIDialog.wmMDIActivate };
-
- {**********************************************************************
-
- When the TMDIDialog window is activated, it also gets the focus.
- We have to activate the child dialog box's keyboard handler and
- pass the focus to the correct dialog box child window. No
- other processing is necessary.
-
- **********************************************************************}
-
- procedure TMDIDialog.wmSetFocus(var Msg: TMessage);
- begin { TMDIDialog.wmSetFocus }
- ActivateDlg (Msg);
- SetChildFocus(Msg);
- Msg.Result := 0
- end { TMDIDialog.wmSetFocus };
-
- {**********************************************************************
-
- This method prevents the TMDIDialog window from being any
- larger or smaller than its child dialog box. This helps keep
- the appearance of the window consistent with that of the
- dialog box it's impersonating on screen.
-
- If the window is not being displayed normally (wParam <>
- SizeNormal), we just pass this message to the default window
- procedure. If we're displaying normally, we compute:
-
- The height of the non-client area;
- The height & width of the dialog;
- The height & width that the window needs to be to accomodate
- all of the above (width of dialog, height of dialog + height
- of the non-client area).
-
- **********************************************************************}
-
- procedure TMDIDialog.wmSize(var Msg: TMessage);
- var
- W, H : integer;
- DialogRect: TRect;
-
- begin { TMDIDialog.wmSize }
- if Msg.wParam = SizeNormal { If we're doing a normal show }
- then
- begin
- with Attr do
- SetWindowPos(HWindow, 0, X, Y, W, H, { Set the window's height without moving it }
- swp_NoZOrder or swp_NoMove);
- MoveWindow(TheDialog^.HWindow, 0, 0, W, { Correctly place the dialog in the window }
- H - NCHeight, TRUE);
- UpdateWindow(HWindow) { Redraw the window immediately }
- end;
- DefWndProc(Msg)
- end { TMDIDialog.wmSize };
-
- {**********************************************************************
-
- This method ensures that the dialog box child control focus
- is properly saved in the case where the MDI window is iconized
- but has never received wm_MDIActivate with wParam = 0.
-
- **********************************************************************}
-
- procedure TMDIDialog.wmSysCommand(var Msg: TMessage);
- var
- CurFocus: HWnd;
- MenuItem: Word;
-
- begin { TMDIDialog.wmSysCommand }
- CurFocus := GetFocus; { Get the window that current has focus }
- if (CurFocus <> 0) and { If there is one that has the focus }
- IsChild(TheDialog^.HWindow, CurFocus) { And it's a child of the dialog }
- then FocusChildHandle := CurFocus; { Then save the child's handle }
- MenuItem := (Msg.wParam and $FFF0);
- if (MenuItem = sc_Maximize) or
- (MenuItem = sc_Size)
- then Msg.Result := 1
- else DefWndProc(Msg)
- end { TMDIDialog.wmSysCommand };
-
- {**********************************************************************
-
- TMDIFrame methods
-
- This method closes & destroys a child window. AChild is a
- pointer to an OWL object representing the child being closed.
- The method first checks the pointer to make sure it's valid,
- then it checks to see if the object's window handle is a child
- of the MDI Client window. If both of these conditions are
- met, the window is destroyed. Otherwise, the method just exits
-
- **********************************************************************}
-
- procedure TMDIFrame.CloseChild(AChild: PWindowsObject);
- begin { TMDIFrame.CloseChild }
- if (AChild <> nil) and
- IsChild(GetClient^.HWindow, AChild^.HWindow)
- then
- if AChild^.CanClose
- then dispose(AChild, Done)
- end { TMDIFrame.CloseChild };
-
- {**********************************************************************
-
- This method is used to find the pointer to the OWL object
- which has the handle passed in TheWnd.
-
- **********************************************************************}
-
- function TMDIFrame.GetChild(TheWnd: HWnd): PWindowsObject;
-
- function HandlesMatch(Window: PWindowsObject): boolean; far;
- begin { HandlesMatch }
- HandlesMatch := Window^.HWindow = TheWnd
- end { HandlesMatch };
-
- begin { TMDIFrame.GetChild }
- GetChild := FirstThat(@HandlesMatch)
- end { TMDIFrame.GetChild };
-
- {**********************************************************************
-
- This method passes the wm_InitMenu message on to the currently
- active MDI child window. This allows us to easily implement
- child-window specific menu handling for MDI child windows,
- especially edit windows.
-
- **********************************************************************}
-
- procedure TMDIFrame.wmInitMenu(var Msg: TMessage);
- var
- CurMDIHwnd: HWnd;
-
- begin { TMDIFrame.wmInitMenu }
- CurMDIHwnd := LoWord(SendMessage(ClientWnd^.HWindow, wm_MDIGetActive, 0, 0));
- if CurMDIHwnd <> 0
- then SendMessage(CurMDIHwnd, wm_InitMenu, Msg.wParam, Msg.lParam);
- Msg.result := 0
- end { TMDIFrame.wmInitMenu };
-
- {**********************************************************************
-
- This method passes the wm_InitMenuPopup message on to the
- currently active MDI child window. This allows us to easily
- implement child-window specific menu handling for MDI child
- windows, especially edit windows.
-
- The MDI child should set Msg.result to 0 if it does not wish
- the frame window to pass the message onto DefWndProc.
-
- **********************************************************************}
-
- procedure TMDIFrame.wmInitMenuPopup(var Msg: TMessage);
- var
- CurMDIHwnd: HWnd;
-
- begin { TMDIFrame.wmInitMenuPopup }
- CurMDIHwnd := LoWord(SendMessage(ClientWnd^.HWindow, wm_MDIGetActive, 0, 0));
- if CurMDIHwnd <> 0
- then SendMessage(CurMDIHwnd, wm_InitMenuPopup, Msg.wParam, Msg.lParam);
- Msg.result := 0
- end { TMDIFrame.wmInitMenuPopup };
-
- begin
- RegisterType(RMDIChild);
- RegisterType(RMDIDialog);
- RegisterType(RMDIFrame);
- end.